package app.server.project.main.service.service.upay.tron;

import app.server.project.core.constants.Def;
import app.server.project.main.service.bo.event.AddRaceValEvent;
import app.server.project.main.service.db.dao.user.BlockNumDao;
import app.server.project.main.service.db.dao.user.BlockTransactionDao;
import app.server.project.main.service.db.dao.user.UserWalletDao;
import app.server.project.main.service.db.entity.cfg.RaceCfgEntity;
import app.server.project.main.service.db.entity.user.BlockTransactionEntity;
import app.server.project.main.service.dto.upay.tron.USDTransactionResp;
import app.server.project.main.service.service.race.CoinType;
import app.server.project.main.service.service.race.DetailType;
import app.server.project.main.service.service.upay.BalanceService;
import app.server.project.main.service.service.upay.UPayType;
import app.server.project.main.service.service.upay.tron.constants.APIKey;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.tron.trident.core.exceptions.IllegalException;

@Slf4j
@Service
@RequiredArgsConstructor
public class BlockService {

    private final static long PERIOD = 1000 * 15;
    private final static long BLOCK_TIME = 1000;

    private final CfgService cfgService;
    private final BlockNumDao blockNumDao;
    private final BlockTransactionDao blockTransactionDao;
    private final UserWalletDao userWalletDao;
    private final BalanceService balanceService;
    private final ApplicationEventPublisher applicationEventPublisher;
    private ConcurrentHashSet<String> dbAddressSet = new ConcurrentHashSet<>();

    private long currentNum;

    public static void main(String[] args) throws IllegalException {
        System.out.println(String.valueOf(1.4d * 33));
    }

    @EventListener(ApplicationReadyEvent.class)
    @Async(Def.CommonPool)
    public void init() throws IllegalException {
        var db = blockNumDao.getBlockNumBy(UPayType.Tron);
        this.currentNum = db.getNum();
        //检查上次区块时间
        if (currentNum == 0) {
            this.addBlockNum(System.currentTimeMillis() - PERIOD);
        }
        var list = this.userWalletDao.getBaseMapper().getAllHumanAddress(UPayType.Tron);
        this.dbAddressSet.addAll(list);
    }

    /**
     * 定时2秒拉取区块数据
     *
     * @return
     */
    @Scheduled(fixedDelay = 1000)
    public void pullBlock() throws IllegalException {
        if (this.currentNum == 0) {
            this.addBlockNum(System.currentTimeMillis() - PERIOD);
            return;
        }
        //时间差,确保交易完成
        //log.info("当前区块:{}", this.currentNum);
//        var now = System.currentTimeMillis();
//        if (now - this.currentNum < PERIOD) {
//            return;
//        }
        //log.info("{}", LocalDateTime.now());
        //开始拉取一个区块交易
        var minTime = this.currentNum;
        var maxTime = System.currentTimeMillis() - PERIOD;
        var now = System.currentTimeMillis();
        //log.info("最小时间--{}:最大时间---{}:现在时间--{}:差值:{}", new Date(minTime), new Date(maxTime), new Date(), now - maxTime);
        String url = String.format(this.cfgService.getBaseUrl() + "/v1/contracts/" + this.cfgService.getUSDTContractAddress() + "/transactions?limit=200&min_block_timestamp=%s&max_block_timestamp=%s", minTime, maxTime);
        //log.info("请求地址:{}", url);
        var ret = HttpRequest.get(url)
                .header("TRON-PRO-API-KEY", APIKey.TX_API_KEY)
                .timeout(60 * 1000)
                .execute().body();
        var resp = JSONUtil.toBean(ret, USDTransactionResp.class);
        this.doTransaction(resp);
        var maxLoop = 100;
        while (maxLoop > 0 && resp.getMeta() != null
                && resp.getMeta().getLinks() != null
                && StrUtil.isNotBlank(resp.getMeta().getLinks().getNext())) {
            ret = HttpRequest.get(resp.getMeta().getLinks().getNext())
                    .header("TRON-PRO-API-KEY", APIKey.TX_API_KEY)
                    .timeout(60 * 1000)
                    .execute().body();
            resp = JSONUtil.toBean(ret, USDTransactionResp.class);
            this.doTransaction(resp);
            maxLoop--;
        }
        //标记成功
        this.addBlockNum(maxTime);
    }

    /**
     * 设置上次查询时间
     */
    private void addBlockNum(long time) {
        var db = blockNumDao.getBlockNumBy(UPayType.Tron);
        db.setNum(time);
        this.currentNum = db.getNum();
    }

    /**
     * 处理交易
     *
     * @param resp
     */
    private void doTransaction(USDTransactionResp resp) {
        for (var item : resp.getData()) {
            try {
                //检查是否是usdt充值
                //log.info("未确认订单{}", item.getTransactionId());
                if (!item.isSuccess()) continue;
                //判断是否本系统钱包地址
                var toAddress = item.getRawData().getContract().get(0).getParameter().getValue().getToAddress();
                var amount = item.getRawData().getContract().get(0).getParameter().getValue().getAmount();
                //检查是否公司钱包
                if (!this.dbAddressSet.contains(toAddress)) continue;
                //1000000=1usdt


                var wallet = userWalletDao.getWalletByAddress(toAddress, UPayType.Tron);
                if (wallet.getUserId() == Def.Zero) continue;
                //检查交易是否被处理
                var transaction = this.blockTransactionDao.getBlockTransactionBy(UPayType.Tron, item.getTxID());
                if (transaction == null) {
                    transaction = BlockTransactionEntity.of(item.getTxID(), wallet.getUserId(), amount, UPayType.Tron);
                    this.blockTransactionDao.saveOrUpdate(transaction);
                } else {
                    continue;
                }
                //添加余额
                this.balanceService.addBalance(wallet.getUserId(), amount);
                //添加代币明细
                var cfg = new RaceCfgEntity();
                cfg.setId(CoinType.USDT_COIN);
                cfg.setRate(UPayType.U_Rate);
                var event = AddRaceValEvent.of(wallet.getUserId(), cfg, amount, DetailType.USDTRecharge.getType());
                event.setBlockType(UPayType.Tron);
                this.applicationEventPublisher.publishEvent(event);
            } catch (Exception e) {
                log.error("处理交易异常:{}", item, e);
            }

        }
    }

    /**
     * 添加地址,方便判断本系统地址
     *
     * @param address
     */
    public void addAddress(String address) {
        this.dbAddressSet.add(address);
    }
}
